package com.wenshi.web.socket;


import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wenshi.common.constant.ModbusConstants;
import com.wenshi.common.utils.DateUtils;
import com.wenshi.common.utils.spring.SpringUtils;
import com.wenshi.system.domain.Alarm;
import com.wenshi.system.domain.DeviceData;
import com.wenshi.system.domain.HydrogenProductionRates;
import com.wenshi.system.domain.SysDevice;
import com.wenshi.system.domain.vo.DeviceVo;
import com.wenshi.system.service.DeviceDataService;
import com.wenshi.system.service.GetDeviceStatusService;
import com.wenshi.system.service.ISysAlarmService;
import com.wenshi.system.service.ISysDeviceService;
import com.wenshi.web.controller.schedule.DealSchedule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import static com.wenshi.system.init.InitMappingMap.*;

@Slf4j
@Component
@ServerEndpoint(value = "/webSocket/{serverId}")
public class WebSocket {
    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * 服务ID
     */
    private String serverId;

   public static ConcurrentLinkedQueue<Alarm> dealMap =  new ConcurrentLinkedQueue<>();

    public static ConcurrentLinkedQueue<HydrogenProductionRates> rateMap =  new ConcurrentLinkedQueue<>();

    private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    private ScheduledFuture<?> task1;
    private ScheduledFuture<?> task2;

    @Autowired
    private GetDeviceStatusService getDeviceStatusService;
    @Autowired
    private ISysDeviceService iSysDeviceService;
    @Autowired
    private ISysAlarmService iSysAlarmService;
    @Autowired
    private DealSchedule dealSchedule;
    @Autowired
    private DeviceDataService deviceDataService;

    /**
     * concurrent包的线程安全Set，用来存放每个客户端对应的WebSocket对象。
     * * 虽然@Component默认是单例模式的，但springboot还是会为每个websocket连接初始化一个bean，所以可以用一个静态set保存起来。
     * *  注：底下WebSocket是当前类名
     */
    private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
    /**
     * 用来存在线连接用户信息
     */
    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<String, Session>();
    /**
     * 链接成功调用的方法
     */
//    @PostConstruct
//    private void intBean(){
//        this.getDeviceStatusService= SpringUtils.getBean(GetDeviceStatusService.class);
//        this.iSysDeviceService=SpringUtils.getBean(ISysDeviceService.class);
//
//    }
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "serverId") String serverId) {
        try {
            this.session = session;
            this.serverId = serverId;
            webSockets.add(this);
            sessionPool.put(serverId, session);
            log.info("【websocket消息】有新的连接,地址是:{}，总数为:{}" ,serverId, webSockets.size());
            this.getDeviceStatusService= SpringUtils.getBean(GetDeviceStatusService.class);
            this.iSysDeviceService=SpringUtils.getBean(ISysDeviceService.class);
            this.iSysAlarmService = SpringUtils.getBean(ISysAlarmService.class);
            this.deviceDataService=SpringUtils.getBean(DeviceDataService.class);
            this.dealSchedule= SpringUtils.getBean(DealSchedule.class);
            this.dealSchedule= SpringUtils.getBean(DealSchedule.class);
            SysDevice byDeviceIp = iSysDeviceService.getByDeviceIp(serverId);
            Long deviceId = byDeviceIp.getDeviceId();
            //推送指定serverId的数据,也就是推送指定ip地址的设备数据
            task1=scheduler.scheduleAtFixedRate(()->{
                try {
////                    sendMessage(serverId, "周期性推送的消息" + System.currentTimeMillis());
//                    modbusAndResultMap.clear();
//                    nameAndResultMap.clear();
//                    resultAndDescMap.clear();
//                    this.getDeviceStatusService= SpringUtils.getBean(GetDeviceStatusService.class);
//                    boolean runPageStatus = getDeviceStatusService.getRunPageStatus(serverId);
//                    DeviceVo deviceVo = new DeviceVo();
//                    deviceVo.setDeviceId(serverId);
//                    ConcurrentHashMap<String, Integer> originalMap = resultAndDescMap.get(serverId);
//                    ConcurrentHashMap<String, Integer> copiedMapForDeviceData = SerializationUtils.clone(originalMap);
//                    if (runPageStatus) {
//                        deviceVo.setDeviceData(copiedMapForDeviceData);
//                        modbusAndResultMap.clear();
//                        nameAndResultMap.clear();
//                        resultAndDescMap.clear();
//                    }
//                    boolean runStatusSwitch = getDeviceStatusService.getRunStatusSwitch(serverId);
//                    ConcurrentHashMap<String, Integer> originalMap1 = resultAndDescMap.get(serverId);
//                    ConcurrentHashMap<String, Integer> copiedMapForDeviceData1 = SerializationUtils.clone(originalMap1);
//                    List<String> collect = copiedMapForDeviceData.keySet().stream().collect(Collectors.toList());
//                    if (runPageStatus||runStatusSwitch){
//                        collect.forEach(copiedMapForDeviceData1::remove);
//                        deviceVo.setDeviceSwitch(copiedMapForDeviceData1);
//
//                        ConcurrentHashMap<String, Integer> deviceData = deviceVo.getDeviceData();
//
//                        Integer runPowerCurrent = deviceData.get(ModbusConstants.runPowerCurrent);
//                        log.info("当前运行电流是:{}A",runPowerCurrent);
//
//                        double currentHCapacity = calculationCurrent(runPowerCurrent);
//                        log.info("当前运行产氢量是:{}(L/h)",currentHCapacity);
//                        deviceVo.setCurrentHproductionCapacity(currentHCapacity);
//
//                        String toJSONString = JSON.toJSONString(deviceVo);
//                        log.info("将推送的数据是,{}",toJSONString);
//                        modbusAndResultMap.clear();
//                        nameAndResultMap.clear();
//                        resultAndDescMap.clear();
//                    }
//                    boolean runStatusAlarm = getDeviceStatusService.getRunStatusAlarm(serverId);
//                    ConcurrentHashMap<String, Integer> originalMap2 = resultAndDescMap.get(serverId);
//                    ConcurrentHashMap<String, Integer> copiedMapForDeviceData2 = SerializationUtils.clone(originalMap2);
//                    if (runStatusAlarm){
//                        collect.forEach(copiedMapForDeviceData2::remove);
//                        deviceVo.setDeviceAlarm(copiedMapForDeviceData2);
//                        String toJSONString = JSON.toJSONString(deviceVo);
//                        sendMessage(serverId,toJSONString);
//                        log.info("最终将推送的数据是,{}",toJSONString);
//                    }
                    DeviceVo deviceVo = createDeviceMsg();
                    String toJSONString = JSON.toJSONString(deviceVo);
                    dealSchedule.dealAlarmThread(deviceId);
                    DeviceData deviceData = WebSocketMsgDeal.dealDeviceMsg(deviceVo.getDeviceData());
                    deviceData.setDeviceId(deviceId);
                    deviceData.setTimestamp(new Date());
//                    boolean save = deviceDataService.saveDeviceData(deviceData);
//                    if (save){
//                        log.info("deviceData更新已存储");
//                    }
                    log.info("deviceData是,{}",JSON.toJSONString(deviceData));
                    sendMessage(serverId,toJSONString);
                    log.info("最终将推送的数据是,{}",toJSONString);
                }catch (Exception e){
                    log.error("执行定时任务发生错误1", e);
                }
            },0,10, TimeUnit.SECONDS);
           task2= scheduler.scheduleAtFixedRate(()->{
                try {
                    dealSchedule.dealHydrogenRateThread();
                }catch (Exception e){
                    log.error("执行定时任务发生错误2", e);
                }
            },0,600, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("WebSocket onOpen异常", e);
        }
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        try {
            webSockets.remove(this);
            sessionPool.remove(this.serverId);
            if (this.task1!=null){
                task1.cancel(true);
            }
            if (this.task2!=null) {
                scheduler.execute(() -> {
                    try {
                        //获取下一个计划执行的时间
                        long delay = task2.getDelay(TimeUnit.SECONDS);
                        if (delay > 0) {
                            Thread.sleep(delay);
                        }
                        task2.get();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        task2.cancel(true);
                    }
                });
            }
            log.info("【websocket消息】连接断开，总数为:" + webSockets.size());
        } catch (Exception e) {
            log.error("WebSocket onClose异常", e);
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("【websocket消息】收到客户端消息:" + message);
    }

    /**
     * 发送错误时的处理
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误,原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 此为单点消息
     */
    public synchronized void sendMessage(String serverId, String message) {
        Session session = sessionPool.get(serverId);

        if (session != null && session.isOpen()) {
            synchronized (session) {
                try {
                    log.info("【{}】服务发送websocket消息，消息内容是:{}", serverId, message);
                    session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 此为单点消息
     */
    public synchronized void sendMessageClient(String serverId, String message) {
        Session session = this.session;

        if (session != null && session.isOpen()) {
            synchronized (session) {
                try {
                    log.info("【{}】服务发送websocket消息，消息内容是:{}", serverId, message);
                    session.getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 将数据主动向前端推送
     *
     * @param serverId 服务id  用作前端作为区别
     * @param message  消息体 JSONObject.toJSONString( 实体类, SerializerFeature.DisableCircularReferenceDetect)
     */
    public void sendMsgToWebSocket(String serverId, String message) {
        //  主动推送数据给前端
        this.sendMessage(serverId, message);
    }

    /**
     * 将数据主动向前端推送
     */
    public void sendAllMsgToWebSocket(String message) {
        webSockets.forEach((webSocket) -> {
            webSocket.sendMessageClient(webSocket.serverId, message);
        });
    }

    /**
     * 计算产氢率
     * @param current 运行电流
     * @return
     */
    public double calculationCurrent(Integer current){
        int currentValue = current.intValue();
        /**
         * p=(Ix6.971x24x60)/1000
         */
        double currentDouble = currentValue * 6.971 * 24 * 60;
        BigDecimal result = new BigDecimal(currentDouble / 1000).setScale(2, RoundingMode.HALF_UP);
        double currentResult = result.doubleValue();
        return currentResult;
    }

    public void  clearMap(){
        modbusAndResultMap.clear();
        nameAndResultMap.clear();
        resultAndDescMap.clear();
    }
    public DeviceVo createDeviceMsg(){
        clearMap();
        boolean runPageStatus = getDeviceStatusService.getRunPageStatus(serverId);
        DeviceVo deviceVo = new DeviceVo();
        deviceVo.setDeviceIp(serverId);
        ConcurrentHashMap<String, Integer> originalMap = resultAndDescMap.get(serverId);
        ConcurrentHashMap<String, Integer> copiedMapForDeviceData = SerializationUtils.clone(originalMap);
        if (runPageStatus) {
            deviceVo.setDeviceData(copiedMapForDeviceData);
            clearMap();
        }
        boolean runStatusSwitch = getDeviceStatusService.getRunStatusSwitch(serverId);
        ConcurrentHashMap<String, Integer> originalMap1 = resultAndDescMap.get(serverId);
        ConcurrentHashMap<String, Integer> copiedMapForDeviceData1 = SerializationUtils.clone(originalMap1);
        List<String> collect = copiedMapForDeviceData.keySet().stream().collect(Collectors.toList());
        if (runPageStatus||runStatusSwitch){
            collect.forEach(copiedMapForDeviceData1::remove);
            deviceVo.setDeviceSwitch(copiedMapForDeviceData1);

            ConcurrentHashMap<String, Integer> deviceData = deviceVo.getDeviceData();

            Integer runPowerCurrent = deviceData.get(ModbusConstants.runPowerCurrent);
            log.info("当前运行电流是:{}A",runPowerCurrent);

            double currentHCapacity = calculationCurrent(runPowerCurrent);
            log.info("当前运行产氢量是:{}(L/h)",currentHCapacity);
            deviceVo.setCurrentHproductionCapacity(currentHCapacity);


            HydrogenProductionRates hydrogenProductionRates = new HydrogenProductionRates();
            hydrogenProductionRates.setDeviceIp(serverId);
            hydrogenProductionRates.setTimestamp(new Date());
            hydrogenProductionRates.setProductionRate(currentHCapacity);
            log.info("本次采集的产氢率：{}",currentHCapacity);
            rateMap.offer(hydrogenProductionRates);

            String toJSONString = JSON.toJSONString(deviceVo);
            log.info("将推送的数据是,{}",toJSONString);
            clearMap();
        }
        boolean runStatusAlarm = getDeviceStatusService.getRunStatusAlarm(serverId);
        ConcurrentHashMap<String, Integer> originalMap2 = resultAndDescMap.get(serverId);
        ConcurrentHashMap<String, Integer> copiedMapForDeviceData2 = SerializationUtils.clone(originalMap2);
        QueryWrapper<SysDevice> queryWrapper = new QueryWrapper<>();
        if (runStatusAlarm){
            collect.forEach(copiedMapForDeviceData2::remove);
            for (String key : copiedMapForDeviceData2.keySet()) {
                Integer integer = copiedMapForDeviceData2.get(key);
                if (0!=integer){
                    Alarm alarm = new Alarm();
                    SysDevice sysDevice = iSysDeviceService.getOne(queryWrapper.eq("device_ip", serverId));
                    alarm.setDeviceId(sysDevice.getDeviceId());
                    alarm.setAlarmName(key);
                    Map<String, String> map = nameAndAlarmCodeMap.get(key);
                    String desc = nameAndDescMap.get(key);
                    String alarmCode = map.get(desc);
                    alarm.setAlarmDesc(desc);
                    alarm.setAlarmTime(DateUtils.getTime());
                    alarm.setClearTime(null);
                    alarm.setAlarmType(alarmCode);
                    alarm.setAlarmStatus(false);
                    dealMap.offer(alarm);
//                    log.info("dealmapsize:{}",dealMap.size());
                }
            }
            deviceVo.setDeviceAlarm(copiedMapForDeviceData2);
            clearMap();
        }
        boolean runStatusVoltage = getDeviceStatusService.getSingleVoltageList(serverId);
        ConcurrentHashMap<String, Integer> originalMap3 = resultAndDescMap.get(serverId);
        ConcurrentHashMap<String, Integer> copiedMapForDeviceData3 = SerializationUtils.clone(originalMap3);
        if (runStatusVoltage){
            collect.forEach(copiedMapForDeviceData3::remove);
            deviceVo.setSingleVoltageList(copiedMapForDeviceData3);
        }
        return deviceVo;
    }
}
